home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MACD 5
/
MACD 5.bin
/
workbench
/
tools
/
czesc_2
/
makepatch
/
sources
/
makepatch.asm
next >
Wrap
Assembly Source File
|
1993-02-11
|
27KB
|
816 lines
;****** Auto-Revision Header (do not edit) *******************************
;*
;* © Copyright by PetiWare
;*
;* Filename : MakePatch.asm
;* Created on : 16-Jan-93
;* Created by : Peter Simons
;* Current revision : V0.017
;*
;*
;* Purpose: Erstellt aus einem Quell- und Zielfile einen Patch, mit dem
;* man das Original zur Zielversion modifizieren kann.
;*
;*
;* V0.017 : Fehler in GeneratePatch() gefunden, der auftrat wenn das neue
;* File kürzer war, als das Original!
;*
;* V0.016 : PatchHeader verbessert: CRCs und Filelängen werden mit ab-
;* gespeichert!
;*
;* V0.015 : Speicherreservierungen von PUBLIC auf ANY umgestellt, sodaß
;* sie auch mit Virtual Memory funktionieren.
;*
;* V0.014 : Wenn das Patchfile erstellt ist, wird der ProgressIndikator
;* gelöscht.
;*
;* V0.013 : Wird kein Patchname angegeben, wird an den Namen des
;* "NewVersion"-Files der String ".ptch" angehängt.
;*
;* V0.012 : MakePatch gibt jetzt eine (C)-Meldung und den Status aus,
;* bevor der Patch erstellt wird.
;*
;* V0.011 : Reservierungen umgestellt auf AllocRemember()
;*
;* V0.010 : Die Erstellung des Patchfiles läßt sich über CTRL/C ab-
;* brechen.
;*
;* V0.009 : Einfacher ProgressIndikator hinzugefügt
;*
;* V0.008 : Filelänge wird mit abgespeichert.
;*
;* V0.007 : GeneratePatchData() fast völlig umgeschrieben.
;*
;* V0.006 : Neuer Chunk: DT_INSERTED
;*
;* V0.005 : Chunknamen in die richtige Vergangenheitsform umbenannt:
;* SKIP --> SKIPPED
;*
;* V0.004 : Die Patchdaten sind in Chunks aufgebaut: DT_ORIGINAL,
;* DT_PATCHED, DT_SKIP, DT_NOMOREDATA
;*
;* V0.003 : GeneratePatchData() erkennt folgende Fälle sehr zuverlässig:
;* Daten eingefügt, Daten herausgenommen und Daten einfach über-
;* schrieben.
;*
;* V0.002 : Find(), FindFirstDifference() und Compare() hinzugefügt
;*
;* V0.001 : Die in der Kommandozeile angegebenen Files werden geladen
;* und die benötigten Puffer reserviert.
;*
;* V0.000 : --- Initial release ---
;*
;*************************************************************************
;
;
;
;***************************************************************************
;* *
;* SEKTION: Labels, Macros, Switches, Structures *
;* *
;***************************************************************************
;-------------------------------------- Switches -----------
MC68000
NEWSYNTAX
EXEOBJ
OBJFILE "RAM:MakePatch"
DEBUG on
SYM
MULTIPASS
; VERBOSEOPTIM
;-------------------------------------- Structures --------
PatchChunk CLRSO
pc_DataType SO.w 1
pc_ChunkLen SO.l 1
pc_ChunkData SOVAL
pc_SIZEOF SOVAL
gp_Struct CLRFO
gp_Patch fo.l 1
gp_Target fo.l 1
gp_Original fo.l 1
gp_PatchLen fo.l 1
gp_TargetLen fo.l 1
gp_OriginalLen fo.l 1
gp_SIZEOF FOVAL
;-------------------------------------- Labels ------------
MACLIB "DATAS:Includes.preass"
MACLIB "DATAS:Offsets.preass"
INCLUDE "A:Sources/PetiToolLibrary/PToolLibrary.i"
KICKVERSION equ 37
SEARCHLENGTH equ (2*pc_SIZEOF)+4
;-------------------------------------- Datatypes ---------
DT_NOMOREDATA equ 0
DT_ORIGINAL equ 1
DT_PATCHED equ 2
DT_SKIPPED equ 3
DT_INSERTED equ 4
;-------------------------------------- Macros ------------
REVISION MACRO
dc.b "0.017"
ENDM
REVDATE MACRO
dc.b "11-Feb-93"
ENDM
PUSHM MACRO
saveregs setrl \1
movem.l saveregs,-(SP)
ENDM
POPM MACRO
movem.l (SP)+,saveregs
ENDM
;***************************************************************************
;* *
;* SEKTION: Programm *
;* *
;***************************************************************************
;---------------------- Systemumgebung initialisieren ---------------------
START: lea (DATA_Seg,PC),a5
BASEREG a5,DATA_Seg
lea (ptoollibname,PC),a1
moveq #1,d0
move.l ($4).W,a6
jsr (_LVOOpenLibrary,a6)
move.l d0,(_PToolBase)
bne.b .PToolLibOkay
bsr _NoPToolLibrary
bra .Exit
.PToolLibOkay move.l d0,a6
move.l (ptl_DOSBase,a6),(_DOSBase)
move.l (ptl_IntuitionBase,a6),(_IntuitionBase)
move.l #CCF_OS_VERSION,d0
move.w #KICKVERSION,d1
jsr (_LVOCheckConfig,a6)
tst.l d0
beq.b .ConfigOkay
lea (PrgName,PC),a0
moveq #0,d2
jsr (_LVODisplayConfigErrorDos,a6)
bra .ClosePToolLib
.ConfigOkay
;------------------------------ Argument-Parsing --------------------------
lea (RDArgsTemplate,PC),a0
move.l a0,d1
lea (_Original,PC),a0
move.l a0,d2
moveq #0,d3
move.l (_DOSBase,PC),a6
jsr (_LVOReadArgs,a6)
move.l d0,(_RDArgsStruct)
bne.b .RDArgsOkay
moveq #0,d0
moveq #0,d1
lea (PrgName,PC),a0
move.l (_PToolBase,PC),a6
jsr (_LVOErrorHandleDos,a6)
bra .ClosePToolLib
.RDArgsOkay tst.l (_PatchFile)
bne.b .PatchNameOkay
move.l (_NewVersion,PC),a0
bsr _Len
addq.l #5,d0
move.l #MEMF_ANY,d1
lea (_RememberKey,PC),a0
move.l (_IntuitionBase,PC),a6
jsr (_LVOAllocRemember,a6)
move.l d0,(_PatchFile)
move.l (_NewVersion,PC),a0
move.l d0,a1
... move.b (a0)+,(a1)+
bne.b ...
subq.w #1,a1
move.b #'.',(a1)+
move.b #'p',(a1)+
move.b #'t',(a1)+
move.b #'c',(a1)+
move.b #'h',(a1)+
clr.b (a1)+
.PatchNameOkay
;------------------------------ Status ausgeben ---------------------------
move.l (_PatchFile,PC),-(SP)
move.l (_NewVersion,PC),-(SP)
move.l (_Original,PC),-(SP)
pea (PrgName,PC)
lea (Status,PC),a0
move.l SP,a1
move.l (_PToolBase,PC),a6
jsr (_LVOPrintFSimple,a6)
add.w #4*4,SP
;----------------------------- Puffer reservieren -------------------------
move.l (_Original,PC),a0
jsr (_LVOLoadFile,a6)
move.l d0,(_SourceBuffer)
bne.b .SourceOkay
moveq #1,d0
move.l (_Original,PC),-(SP)
move.l SP,a1
bsr _ErrorHandle
addq.w #4,SP
bra .FreeRDArgs
.SourceOkay move.l (_NewVersion,PC),a0
jsr (_LVOLoadFile,a6)
move.l d0,(_TargetBuffer)
bne.b .TargetOkay
moveq #1,d0
move.l (_NewVersion,PC),-(SP)
move.l SP,a1
bsr _ErrorHandle
addq.w #4,SP
bra .FreeSourceBuffer
.TargetOkay move.l (_TargetBuffer,PC),a0
move.l (a0),d2
move.l d2,d0
move.l #MEMF_ANY,d1
lea (_RememberKey,PC),a0
move.l (_IntuitionBase,PC),a6
jsr (_LVOAllocRemember,a6)
move.l d0,(_PatchBuffer)
bne.b .PatchOkay
moveq #2,d0
pea (AnyMemStr,PC)
move.l d2,-(SP)
move.l SP,a1
bsr _ErrorHandle
addq.w #8,SP
bra .FreeTargetBuffer
.PatchOkay
;------------------------------ Patch erstellen ---------------------------
move.l (_SourceBuffer,PC),a0
move.l (a0)+,d0
move.l (_TargetBuffer,PC),a1
move.l (a1)+,d1
move.l (_PatchBuffer,PC),a2
move.l d1,d2
bsr _GeneratePatchData
bsr _ClearLine
cmp.l #-1,d0
bne.b .NoBreak
bsr _BreakError
bra .FreeBuffers
.NoBreak move.l d0,d6
bne.b .SavePatchData
moveq #11,d0
pea (PatchTooLong,PC)
move.l SP,a1
bsr _ErrorHandle
addq.w #4,SP
bra .FreeBuffers
;----------------------------- ZielPuffer speichern -----------------------
.SavePatchData move.l (_PatchFile,PC),d1
move.l #MODE_NEWFILE,d2
move.l (_DOSBase,PC),a6
jsr (_LVOOpen,a6)
ERRORHANDLE d5,0,(.FreeBuffers,PC)
move.l (_TargetBuffer,PC),a0
move.l (a0)+,d0
move.l (_PToolBase,PC),a6
jsr (_LVOCRC16Buffer,a6)
move.w d0,-(SP) ; OriginalCRC
move.l (_SourceBuffer,PC),a0
move.l (a0)+,d0
jsr (_LVOCRC16Buffer,a6)
move.w d0,-(SP) ; PatchedCRC
move.l (_TargetBuffer,PC),a0
move.l (a0),-(SP) ; PatchedLength
move.l d5,d1
move.l SP,d2
moveq #8,d3
move.l (_DOSBase,PC),a6
jsr (_LVOWrite,a6) ; Header speichern
add.w #8,SP
cmp.l #-1,d0
beq.b .SaveError
move.l d5,d1
move.l (_PatchBuffer,PC),d2
move.l d6,d3
move.l (_PToolBase,PC),a6
jsr (_LVOSaveBreak,a6)
cmp.l #-1,d0
bne.b .CloseDestFile
move.l (_DOSBase,PC),a6
jsr (_LVOIoErr,a6)
tst.l d0
bne.b .SaveError
bsr _BreakError
.SaveError moveq #11,d0
pea (SaveError,PC)
move.l SP,a1
bsr _ErrorHandle
addq.w #4,SP
.CloseDestFile move.l d5,d1
move.l (_DOSBase,PC),a6
jsr (_LVOClose,a6)
;-------------------------- Systemumgebung freigeben ----------------------
.FreeBuffers lea (_RememberKey,PC),a0
moveq #-1,d0
move.l (_IntuitionBase,PC),a6
jsr (_LVOFreeRemember,a6)
.FreeTargetBuffer:
move.l (_TargetBuffer,PC),a1
move.l ($4).w,a6
jsr (_LVOFreeVec,a6)
.FreeSourceBuffer:
move.l (_SourceBuffer,PC),a1
move.l ($4).w,a6
jsr (_LVOFreeVec,a6)
.FreeRDArgs move.l (_RDArgsStruct,PC),d1
beq.b .ClosePToolLib
move.l (_DOSBase,PC),a6
jsr (_LVOFreeArgs,a6)
.ClosePToolLib move.l (_PToolBase,PC),a1
move.l ($4).W,a6
jsr (_LVOCloseLibrary,a6)
.Exit moveq #0,d0
rts
;***************************************************************************
;* *
;* SEKTION: Unterroutinen *
;* *
;***************************************************************************
;-------------------------------- Patchgeneration -------------------------
_GeneratePatchData:
; Erstellt das Patchfile, mit dem aus dem Originalfile
; das Zielfile erstellt werden kann.
;
; Parameter: A0=&Original A1=&Target A2=&PatchBuffer
; D0=&OriginalLen D1=&Targetlen D2=&PatchBufsize
; Result : D0=PatchLength
; Kommentar: Wenn als Patchlength eine Null zurückgegeben
; wird, waere der Patch länger geworden, als das
; komplett neue File!!
; Bei einer -1L trat ein Break auf!
PUSHM d2-d7/a2-a3/a6
moveq #0,d6 ; PatchLenCounter
moveq #0,d7
subq.l #1,d2 ; Pufferlänge sicherheits-
link.w a4,#gp_SIZEOF ; halber kleiner annehmen
movem.l d0-d2/a0-a2,(gp_OriginalLen,a4)
.FindDiffLoop move.l (gp_Original,a4),a0
move.l (gp_OriginalLen,a4),d0
move.l (gp_Target,a4),a1
move.l (gp_TargetLen,a4),d1
bsr _FindFirstDifference
cmp.l #-1,d0
beq .NoMoreDiffs
tst.l d0
beq.b .NoOriginalData
bsr _WriteOriginalChunk
cmp.l #-1,d0
beq .exit
.NoOriginalData cmp.l #SEARCHLENGTH,(gp_TargetLen,a4)
bmi .WriteRestInserted
move.l (gp_Original,a4),a2
move.l (gp_OriginalLen,a4),d2
sub.l #SEARCHLENGTH,d2
moveq #-1,d3
.ScanLoop move.l d2,d0
bsr _ShowProgress
tst.l d0
beq.b .NoBreak
moveq #-1,d7
bra .exit ; CTRL/C???
.NoBreak subq.l #1,d2
beq .WriteRestInserted
addq.l #1,d3
lea (a2,d3.l),a1
move.l (gp_Target,a4),a0
move.l (gp_TargetLen,a4),d0
moveq #SEARCHLENGTH,d1
bsr _Find
cmp.l #-1,d0
beq.b .ScanLoop
tst.l d0
beq.b .WriteSkipChunk
bsr _WriteInsertedChunk
cmp.l #-1,d0
beq .exit
.WriteSkipChunk move.l d3,d0
beq .FindDiffLoop
bsr _WriteSkippedChunk
cmp.l #-1,d0
bne .FindDiffLoop
bra .exit
.NoMoreDiffs move.l (gp_OriginalLen,a4),d0
move.l (gp_TargetLen,a4),d1
cmp.l d0,d1
bpl.b .SizeOkay
move.l d1,d0
.SizeOkay bsr _WriteOriginalChunk
cmp.l #-1,d0
beq .exit
.WriteRestInserted:
move.l (gp_TargetLen,a4),d0
bsr _WriteInsertedChunk
cmp.l #-1,d0
beq .exit
.finished subq.l #pc_SIZEOF,(gp_PatchLen,a4)
bmi.b .exit
move.l (gp_Patch,a4),a0
move.w #DT_NOMOREDATA,(a0)+
clr.l (a0)+
addq.l #pc_SIZEOF,d6
move.l d6,d7
.exit move.l d7,d0
unlk a4
POPM
rts
_WriteOriginalChunk:
tst.l d0
beq.b .exit
subq.l #pc_SIZEOF,(gp_PatchLen,a4)
bmi.b .ErrorExit
move.l (gp_Patch,a4),a0
move.w #DT_ORIGINAL,(a0)+
move.l d0,(a0)+
move.l a0,(gp_Patch,a4)
add.l d0,(gp_Original,a4)
sub.l d0,(gp_OriginalLen,a4)
add.l d0,(gp_Target,a4)
sub.l d0,(gp_TargetLen,a4)
add.l #pc_SIZEOF,d6
.exit rts
.ErrorExit moveq #-1,d0
bra.b .exit
_WriteInsertedChunk:
tst.l d0
beq.b .exit
subq.l #pc_SIZEOF,(gp_PatchLen,a4)
sub.l d0,(gp_PatchLen,a4)
bmi.b .ErrorExit
move.l (gp_Target,a4),a0
move.l (gp_Patch,a4),a1
move.w #DT_INSERTED,(a1)+
move.l d0,(a1)+
bsr _CopyChunk
move.l a0,(gp_Target,a4)
sub.l d0,(gp_TargetLen,a4)
btst.l #0,d0
beq.b .NoOddLength
subq.l #1,(gp_PatchLen,a4)
bmi .exit
clr.b (a1)+
addq.l #1,d6
.NoOddLength move.l a1,(gp_Patch,a4)
addq.l #pc_SIZEOF,d6
add.l d0,d6
.exit rts
.ErrorExit moveq #-1,d0
bra.b .exit
_WriteSkippedChunk:
tst.l d0
beq.b .exit
subq.l #pc_SIZEOF,(gp_PatchLen,a4)
bmi.b .ErrorExit
move.l (gp_Patch,a4),a0
move.w #DT_SKIPPED,(a0)+
move.l d0,(a0)+
move.l a0,(gp_Patch,a4)
addq.l #pc_SIZEOF,d6
add.l d0,(gp_Original,a4)
sub.l d0,(gp_OriginalLen,a4)
.exit rts
.ErrorExit moveq #-1,d0
bra.b .exit
_WritePatchedChunk:
tst.l d0
beq.b .exit
bsr _WriteInsertedChunk
cmp.l #-1,d0
beq.b .exit
bra _WriteSkippedChunk
.exit rts
_CopyChunk move.l d0,-(SP)
beq.b .exit
... move.b (a0)+,(a1)+
subq.l #1,d0
bne.b ...
.exit move.l (SP)+,d0
rts
;---------------------------------- Diverses ------------------------------
_Len:
; Funktion : Bestimmt die Länge eines mit Null
; abgeschlossenen Textes
;
; Parameter: A0=&Textpuffer
; Result : D0=Länge *INKLUSIVE* des Nullbytes
; Kommentar: Verändert keine Register ausser D0!!!
pea (a0) ; Register retten
.loop tst.b (a0)+
bne.b .loop ; aktuelles Zeichen gleich Null?
move.l a0,d0
move.l (SP)+,a0
sub.l a0,d0 ; Länge zurückgeben
rts
_ClearLine:
; Löscht die aktuelle Zeile (ProgressIndikator entfernen!)
;
;
; Parameter: keine
; Result : keine
PUSHM d0-d1/a0-a1
lea (ClrLine,PC),a0
move.l (_PToolBase,PC),a6
jsr (_LVOPrintFSimple,a6)
POPM
rts
_ShowProgress:
; Gibt in die aktuelle Zeile den ProgressIndikator aus und
; testet auf CTRL+C.
;
; Parameter: D0=Bytes to go
; Result : D0=SignalMask
PUSHM d1/a0-a1/a6
and.l #$FFFFFFF0,d0
cmp.l (.last,PC),d0
beq.b .DetectBreak
lea (Progress,PC),a0
move.l d0,-(SP)
move.l SP,a1
move.l (_PToolBase,PC),a6
jsr (_LVOPrintFSimple,a6)
addq.w #4,SP
.DetectBreak move.l #SIGBREAKF_CTRL_C,d1
move.l (_DOSBase,PC),a6
jsr (_LVOCheckSignal,a6)
POPM
rts
.last ds.l 1
;----------------------------- Suchen und Vergleichen ---------------------
_FindFirstDifference:
; Findet das erste Byte, das im Zielpuffer anders ist, als
; im Quellpuffer.
;
; Parameter: A0=&Original A1=&Target
; D0=&OriginalLen D1=&Targetlen
; Result : D0=Offset im Quellpuffer
; Kommentar: Ein Rückgabewert von -1L bedeutet, das kein
; Unterschied mehr gefunden werden konnte, bevor
; ein Puffer zu Ende war.
PUSHM d1/a0-a1
tst.l d0
beq.b .ErrorExit
tst.l d1
beq.b .ErrorExit
... cmp.b (a0)+,(a1)+
bne.b .FoundDiff
subq.l #1,d0
beq.b .ErrorExit
subq.l #1,d1
beq.b .ErrorExit
bra.b ...
.FoundDiff move.l a0,d0
movem.l (SP),saveregs
sub.l a0,d0
subq.l #1,d0
.exit POPM
rts
.ErrorExit moveq #-1,d0
bra.b .exit
_Find:
; Sucht eine Bytefolge in einem Puffer
;
; Parameter: A0=&Puffer A1=&String D0=Pufferlength
; D1=StringLänge
; Result : D0=Offset im Puffer oder -1L
PUSHM d1-d4/a0-a3
move.l a0,a3
moveq #0,d4 ; ReturnCode
move.l d0,d2
beq.b .Exit
move.l d1,d3
beq.b .Exit
sub.l d1,d2
bmi.b .Exit
beq.b .SameLength
addq.l #1,d2
moveq #0,d0
moveq #0,d1
move.b (a1),d1
.FindFirstByte move.b (a0),d0
cmp.b d0,d1
beq.b .ByteFound
.NotTheSame addq.w #1,a0
subq.l #1,d2
beq.b .Exit
bra.b .FindFirstByte
.ByteFound move.l d3,d0
bsr _Compare
tst.l d0
beq.b .NotTheSame
move.l d0,d4
.Exit moveq #-1,d0
tst.l d4
beq.b .NotFound
move.l a0,d0
sub.l a3,d0
.NotFound POPM
rts
.SameLength move.l d1,d0
bsr _Compare
move.l d0,d4
bra.b .Exit
_Compare:
; Funktion : Vergleicht zwei Bytefolgen
;
; Parameter: A0=&String1 A1=&String2 D0=Länge des kürzeren
; Result : D0=Gleich (-1) oder verschieden (0)
PUSHM d1-d2/a0-a1
move.l d0,d2
subq.l #1,d2
moveq #0,d0
moveq #0,d1
.loop move.b (a0)+,d0
move.b (a1)+,d1 ; compare them
cmp.b d0,d1
dbne d2,.loop
move.w d2,d0
bmi.s .same
moveq #0,d0
.same ext.l d0
.exit POPM
rts
;-------------------------------- Fehlerhandhabung ------------------------
_BreakError:
; Gibt den "Received CTRL/C"-String aus
;
; Parameter: keine
; Result : keins
PUSHM d0-d1/a0-a1/a6
lea (PrgName,PC),a0
moveq #0,d1
moveq #5,d0
move.l (_PToolBase,PC),a6
jsr (_LVOErrorHandleDos,a6)
POPM
rts
_ErrorHandle:
; Erzeut über ErrorHandleIntuition() die Fehlermeldung.
;
; Parameter: A1=&DataFlow D0=Errornumber
; Result : keins
PUSHM d0-d1/a0-a2/a6
lea (PrgName,PC),a0
sub.l a2,a2
move.l (_PToolBase,PC),a6
jsr (_LVOErrorHandleIntuition,a6)
POPM
rts
_NoPToolLibrary:
; Gibt einen Fehler aus, falls die PTool.Library nicht ge-
; öffnet werden konnte.
;
; Parameter: keine
; Result : keins
lea (dosname,PC),a1
move.l ($4).W,a6
jsr (_LVOOldOpenLibrary,a6)
tst.l d0
beq.b .exit
move.l d0,a6
jsr (_LVOOutput,a6)
move.l d0,d1
beq.b .CloseDosLib
lea (PrgName,PC),a0
move.l a0,d2
moveq #NoPToolError_len,d3
jsr (_LVOWrite,a6)
.CloseDosLib move.l a6,a1
move.l ($4).W,a6
jsr (_LVOCloseLibrary,a6)
.exit rts
;***************************************************************************
;* *
;* SEKTION: Daten *
;* *
;***************************************************************************
DATA_Seg:
dc.b "$VER: "
PrgName dc.b "MakePatch v"
REVISION
dc.b " ("
REVDATE
dc.b ")"
dc.b 0
dc.b " requires the PTool.Library!!",$0A,$0A
NoPToolError_len equ (*-PrgName)
ptoollibname CSTR "ptool.library"
dosname CSTR "dos.library"
AnyMemStr CSTR "RAM"
RDArgsTemplate CSTR "Original/A,NewVersion/A,PatchFile"
PatchTooLong CSTR "Sorry, but the patch would have been longer, than the new-version!"
SaveError CSTR "File couldn't be saved succesfully!"
Progress CSTR $0D,"Progress: %8.lu byte to go..."
ClrLine CSTR $9B,$4D,$0D
Status dc.b $9B,"1;33",$6D,"%s",$9B,$6D," - coding by Peter Simons",$0A
dc.b "old version-file: %s",$0A
dc.b "new version-file: %s",$0A
dc.b " patch-file: %s",$0A
dc.b 0
CNOP 0,4
_PToolBase ds.l 1
_DOSBase ds.l 1
_IntuitionBase ds.l 1
_RememberKey ds.l 1
_RDArgsStruct ds.l 1
_SourceBuffer ds.l 1
_TargetBuffer ds.l 1
_PatchBuffer ds.l 1
_Original ds.l 1 ; Array für ReadArgs()
_NewVersion ds.l 1
_PatchFile ds.l 1
END